// @ts-check
export default class WxCanvas {
    ctx;
    type;
    canvasId;
    canvasNode;
    stepList = [];
    canvasPrototype = {};

    constructor(type, ctx, canvasId, isNew, canvasNode) {
        this.ctx = ctx;
        this.canvasId = canvasId;
        this.type = type;
        if (isNew) {
            this.canvasNode = canvasNode || {};
        }
    }

    set width (w) {
        if (this.canvasNode) {
            this.canvasNode.width = w;
            // 经测试，在 2d 接口中如果不设置这个值，IOS 端有一定几率会出现图片显示不全的情况。
            this.canvasNode._width = w;
        }
    }

    get width () {
        if (this.canvasNode) return this.canvasNode.width;
        return 0;
    }

    set height (h) {
        if (this.canvasNode) {
            this.canvasNode.height = h;
            // 经测试，在 2d 接口中如果不设置这个值，IOS 端有一定几率会出现图片显示不全的情况。
            this.canvasNode._height = h;
        }
    }

    get height () {
        if (this.canvasNode) return this.canvasNode.height;
        return 0;
    }

    set lineWidth (args) {
        this.canvasPrototype.lineWidth = args;
        this.stepList.push({
            action: "lineWidth",
            args,
            actionType: "set",
        });
    }

    get lineWidth () {
        return this.canvasPrototype.lineWidth;
    }

    set lineCap (args) {
        this.canvasPrototype.lineCap = args;
        this.stepList.push({
            action: "lineCap",
            args,
            actionType: "set",
        });
    }

    get lineCap () {
        return this.canvasPrototype.lineCap;
    }

    set lineJoin (args) {
        this.canvasPrototype.lineJoin = args;
        this.stepList.push({
            action: "lineJoin",
            args,
            actionType: "set",
        });
    }

    get lineJoin () {
        return this.canvasPrototype.lineJoin;
    }

    set miterLimit (args) {
        this.canvasPrototype.miterLimit = args;
        this.stepList.push({
            action: "miterLimit",
            args,
            actionType: "set",
        });
    }

    get miterLimit () {
        return this.canvasPrototype.miterLimit;
    }

    set lineDashOffset (args) {
        this.canvasPrototype.lineDashOffset = args;
        this.stepList.push({
            action: "lineDashOffset",
            args,
            actionType: "set",
        });
    }

    get lineDashOffset () {
        return this.canvasPrototype.lineDashOffset;
    }

    set font (args) {
        this.canvasPrototype.font = args;
        this.ctx.font = args;
        this.stepList.push({
            action: "font",
            args,
            actionType: "set",
        });
    }

    get font () {
        return this.canvasPrototype.font;
    }

    set textAlign (args) {
        this.canvasPrototype.textAlign = args;
        this.stepList.push({
            action: "textAlign",
            args,
            actionType: "set",
        });
    }

    get textAlign () {
        return this.canvasPrototype.textAlign;
    }

    set textBaseline (args) {
        this.canvasPrototype.textBaseline = args;
        this.stepList.push({
            action: "textBaseline",
            args,
            actionType: "set",
        });
    }

    get textBaseline () {
        return this.canvasPrototype.textBaseline;
    }

    set fillStyle (args) {
        this.canvasPrototype.fillStyle = args;
        this.stepList.push({
            action: "fillStyle",
            args,
            actionType: "set",
        });
    }

    get fillStyle () {
        return this.canvasPrototype.fillStyle;
    }

    set strokeStyle (args) {
        this.canvasPrototype.strokeStyle = args;
        this.stepList.push({
            action: "strokeStyle",
            args,
            actionType: "set",
        });
    }

    get strokeStyle () {
        return this.canvasPrototype.strokeStyle;
    }

    set globalAlpha (args) {
        this.canvasPrototype.globalAlpha = args;
        this.stepList.push({
            action: "globalAlpha",
            args,
            actionType: "set",
        });
    }

    get globalAlpha () {
        return this.canvasPrototype.globalAlpha;
    }

    set globalCompositeOperation (args) {
        this.canvasPrototype.globalCompositeOperation = args;
        this.stepList.push({
            action: "globalCompositeOperation",
            args,
            actionType: "set",
        });
    }

    get globalCompositeOperation () {
        return this.canvasPrototype.globalCompositeOperation;
    }

    set shadowColor (args) {
        this.canvasPrototype.shadowColor = args;
        this.stepList.push({
            action: "shadowColor",
            args,
            actionType: "set",
        });
    }

    get shadowColor () {
        return this.canvasPrototype.shadowColor;
    }

    set shadowOffsetX (args) {
        this.canvasPrototype.shadowOffsetX = args;
        this.stepList.push({
            action: "shadowOffsetX",
            args,
            actionType: "set",
        });
    }

    get shadowOffsetX () {
        return this.canvasPrototype.shadowOffsetX;
    }

    set shadowOffsetY (args) {
        this.canvasPrototype.shadowOffsetY = args;
        this.stepList.push({
            action: "shadowOffsetY",
            args,
            actionType: "set",
        });
    }

    get shadowOffsetY () {
        return this.canvasPrototype.shadowOffsetY;
    }

    set shadowBlur (args) {
        this.canvasPrototype.shadowBlur = args;
        this.stepList.push({
            action: "shadowBlur",
            args,
            actionType: "set",
        });
    }

    get shadowBlur () {
        return this.canvasPrototype.shadowBlur;
    }

    save () {
        this.stepList.push({
            action: "save",
            args: null,
            actionType: "func",
        });
    }

    restore () {
        this.stepList.push({
            action: "restore",
            args: null,
            actionType: "func",
        });
    }

    setLineDash (...args) {
        this.canvasPrototype.lineDash = args;
        this.stepList.push({
            action: "setLineDash",
            args,
            actionType: "func",
        });
    }

    moveTo (...args) {
        this.stepList.push({
            action: "moveTo",
            args,
            actionType: "func",
        });
    }

    closePath () {
        this.stepList.push({
            action: "closePath",
            args: null,
            actionType: "func",
        });
    }

    lineTo (...args) {
        this.stepList.push({
            action: "lineTo",
            args,
            actionType: "func",
        });
    }

    quadraticCurveTo (...args) {
        this.stepList.push({
            action: "quadraticCurveTo",
            args,
            actionType: "func",
        });
    }

    bezierCurveTo (...args) {
        this.stepList.push({
            action: "bezierCurveTo",
            args,
            actionType: "func",
        });
    }

    arcTo (...args) {
        this.stepList.push({
            action: "arcTo",
            args,
            actionType: "func",
        });
    }

    arc (...args) {
        this.stepList.push({
            action: "arc",
            args,
            actionType: "func",
        });
    }

    rect (...args) {
        this.stepList.push({
            action: "rect",
            args,
            actionType: "func",
        });
    }

    scale (...args) {
        this.stepList.push({
            action: "scale",
            args,
            actionType: "func",
        });
    }

    rotate (...args) {
        this.stepList.push({
            action: "rotate",
            args,
            actionType: "func",
        });
    }

    translate (...args) {
        this.stepList.push({
            action: "translate",
            args,
            actionType: "func",
        });
    }

    transform (...args) {
        this.stepList.push({
            action: "transform",
            args,
            actionType: "func",
        });
    }

    setTransform (...args) {
        this.stepList.push({
            action: "setTransform",
            args,
            actionType: "func",
        });
    }

    clearRect (...args) {
        this.stepList.push({
            action: "clearRect",
            args,
            actionType: "func",
        });
    }

    fillRect (...args) {
        this.stepList.push({
            action: "fillRect",
            args,
            actionType: "func",
        });
    }

    strokeRect (...args) {
        this.stepList.push({
            action: "strokeRect",
            args,
            actionType: "func",
        });
    }

    fillText (...args) {
        this.stepList.push({
            action: "fillText",
            args,
            actionType: "func",
        });
    }

    strokeText (...args) {
        this.stepList.push({
            action: "strokeText",
            args,
            actionType: "func",
        });
    }

    beginPath () {
        this.stepList.push({
            action: "beginPath",
            args: null,
            actionType: "func",
        });
    }

    fill () {
        this.stepList.push({
            action: "fill",
            args: null,
            actionType: "func",
        });
    }

    stroke () {
        this.stepList.push({
            action: "stroke",
            args: null,
            actionType: "func",
        });
    }

    drawFocusIfNeeded (...args) {
        this.stepList.push({
            action: "drawFocusIfNeeded",
            args,
            actionType: "func",
        });
    }

    clip () {
        this.stepList.push({
            action: "clip",
            args: null,
            actionType: "func",
        });
    }

    isPointInPath (...args) {
        this.stepList.push({
            action: "isPointInPath",
            args,
            actionType: "func",
        });
    }

    drawImage (...args) {
        this.stepList.push({
            action: "drawImage",
            args,
            actionType: "func",
        });
    }

    addHitRegion (...args) {
        this.stepList.push({
            action: "addHitRegion",
            args,
            actionType: "func",
        });
    }

    removeHitRegion (...args) {
        this.stepList.push({
            action: "removeHitRegion",
            args,
            actionType: "func",
        });
    }

    clearHitRegions (...args) {
        this.stepList.push({
            action: "clearHitRegions",
            args,
            actionType: "func",
        });
    }

    putImageData (...args) {
        this.stepList.push({
            action: "putImageData",
            args,
            actionType: "func",
        });
    }

    getLineDash () {
        return this.canvasPrototype.lineDash;
    }

    createLinearGradient (...args) {
        return this.ctx.createLinearGradient(...args);
    }

    createRadialGradient (...args) {
        if (this.type === "2d") {
            return this.ctx.createRadialGradient(...args);
        } else {
            return this.ctx.createCircularGradient(...args.slice(3, 6));
        }
    }

    createPattern (...args) {
        return this.ctx.createPattern(...args);
    }

    measureText (...args) {
        return this.ctx.measureText(...args);
    }

    createImageData (...args) {
        return this.ctx.createImageData(...args);
    }

    getImageData (...args) {
        return this.ctx.getImageData(...args);
    }

    async draw (reserve, func) {
        const realstepList = this.stepList.slice();
        this.stepList.length = 0;
        if (this.type === "mina") {
            if (realstepList.length > 0) {
                for (const step of realstepList) {
                    this.implementMinaStep(step);
                }
                realstepList.length = 0;
            }
            this.ctx.draw(reserve, func);
        } else if (this.type === "2d") {
            if (!reserve) {
                this.ctx.clearRect(0, 0, this.canvasNode.width, this.canvasNode.height);
            }
            if (realstepList.length > 0) {
                for (const step of realstepList) {
                    await this.implement2DStep(step);
                }
                realstepList.length = 0;
            }
            if (func) {
                func();
            }
        }
        realstepList.length = 0;
    }

    implementMinaStep (step) {
        switch (step.action) {
            case "textAlign": {
                this.ctx.setTextAlign(step.args);
                break;
            }
            case "textBaseline": {
                this.ctx.setTextBaseline(step.args);
                break;
            }
            default: {
                if (step.actionType === "set") {
                    this.ctx[step.action] = step.args;
                } else if (step.actionType === "func") {
                    if (step.args) {
                        this.ctx[step.action](...step.args);
                    } else {
                        this.ctx[step.action]();
                    }
                }
                break;
            }
        }
    }

    implement2DStep (step) {
        return new Promise((resolve) => {
            if (step.action === "drawImage") {
                const img = this.canvasNode.createImage();
                img.src = step.args[0];
                img.onload = () => {
                    this.ctx.drawImage(img, ...step.args.slice(1));
                    resolve();
                };
            } else {
                if (step.actionType === "set") {
                    this.ctx[step.action] = step.args;
                } else if (step.actionType === "func") {
                    if (step.args) {
                        this.ctx[step.action](...step.args);
                    } else {
                        this.ctx[step.action]();
                    }
                }
                resolve();
            }
        });
    }
}
